package de.ret_world.openrpgstudio.mapeditor;

import buoy.event.CommandEvent;
import buoy.event.SelectionChangedEvent;
import buoy.event.ValueChangedEvent;
import buoy.widget.*;
import de.ret_world.openrpgstudio.editor.Database;
import de.ret_world.openrpgstudio.editor.Main;
import de.ret_world.openrpgstudio.editor.Media;
import de.ret_world.openrpgstudio.editor.Project;
import de.ret_world.openrpgstudio.editor.gui.MediaChooseDialog;
import de.ret_world.openrpgstudio.editor.gui.ScriptArea;
import de.ret_world.openrpgstudio.editor.helpers.Internationalization;
import de.ret_world.openrpgstudio.editor.helpers.ScriptTools;
import de.ret_world.openrpgstudio.editor.plugininterfaces.DatabasePlugin;
import de.ret_world.openrpgstudio.editor.script.ScriptObject;
import de.ret_world.openrpgstudio.mapeditor.scripts.TileSet;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.jse.JsePlatform;

/**
 * DatabasePlugin implementation for creation of TileSets.
 *
 * @author Emu
 */
public class TileSetDatabase extends DatabasePlugin {

    private BSplitPane split;
    private BTabbedPane tabbed;
    private BList list;
    private BSpinner spnWidth;
    private BSpinner spnHeight;
    private BSpinner spnOffset;
    private BSpinner spnGap;
    private TileSetWidget wTileSet;
    private BButton btnRefresh;
    private BButton btnChoose;
    private ScriptArea script;

    @Override
    public void init(BSplitPane container) {
        Internationalization intn = Internationalization.getInstance();

        config = MapEditor.config;
        manager = MapEditor.manager;
        descriptor = MapEditor.descriptor;

        HashMap<String, ScriptObject> scriptMap = Project.getInstance().getDatabase().getScriptMap(TileSet.TYPE);
        Collection<ScriptObject> tilesets;
        if (scriptMap == null) {
            tilesets = new ArrayList<>();
        } else {
            tilesets = scriptMap.values();
        }

        LayoutInfo li = new LayoutInfo(LayoutInfo.WEST, LayoutInfo.HORIZONTAL, new Insets(5, 5, 5, 5), null);

        split = container;
        split.setDividerLocation(Integer.parseInt(config.getProperty("gui.divider", "175")));

        //Left side
        BorderContainer left = new BorderContainer();
        left.add(new BLabel(intn.getString("ME.TILESETS")), BorderContainer.NORTH, li);
        list = new BList(tilesets);
        BScrollPane scrl = new BScrollPane(BScrollPane.SCROLLBAR_NEVER, BScrollPane.SCROLLBAR_AS_NEEDED);
        scrl.setContent(list);
        scrl.setForceHeight(true);
        list.addEventLink(SelectionChangedEvent.class, this, "selectionChanged");
        left.add(BOutline.createEtchedBorder(scrl, false), BorderContainer.CENTER, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        RowContainer rc = new RowContainer();
        BButton btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("plus.png")));
        btn.addEventLink(CommandEvent.class, this, "addTileSet");
        rc.add(btn, li);
        btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("wrench.png")));
        btn.addEventLink(CommandEvent.class, this, "editTileSet");
        rc.add(btn, li);
        btn = new BButton(new ImageIcon(manager.getPluginClassLoader(manager.getRegistry().getPluginDescriptor(Main.PLUGIN_ID)).getResource("minus.png")));
        btn.addEventLink(CommandEvent.class, this, "removeTileSet");
        rc.add(btn, li);
        left.add(rc, BorderContainer.SOUTH, li);

        split.add(left, 0);

        //Right side
        tabbed = new BTabbedPane();
        tabbed.addEventLink(SelectionChangedEvent.class, this, "tabChanged");

        FormContainer right = new FormContainer(new double[]{0, 1}, new double[]{1});
        ColumnContainer cc = new ColumnContainer();

        cc.add(new BLabel(intn.getString("ME.TILESIZE")), li);
        rc = new RowContainer();
        spnWidth = new BSpinner(32, 8, 128, 2);
        spnWidth.addEventLink(ValueChangedEvent.class, this, "valueChanged");
        rc.add(spnWidth);
        rc.add(new BLabel("x"));
        spnHeight = new BSpinner(32, 8, 128, 2);
        spnHeight.addEventLink(ValueChangedEvent.class, this, "valueChanged");
        rc.add(spnHeight);
        cc.add(rc, li);

        cc.add(new BLabel(intn.getString("ME.OFFSET")), li);
        spnOffset = new BSpinner(0, 0, 128, 1);
        spnOffset.addEventLink(ValueChangedEvent.class, this, "valueChanged");
        cc.add(spnOffset, li);
        cc.add(new BLabel(intn.getString("ME.GAP")), li);
        spnGap = new BSpinner(0, 0, 128, 1);
        spnGap.addEventLink(ValueChangedEvent.class, this, "valueChanged");
        cc.add(spnGap, li);
        btnRefresh = new BButton(intn.getString("ME.REFRESH"));
        btnRefresh.addEventLink(CommandEvent.class, this, "refreshTSW");
        cc.add(btnRefresh, li);

        right.add(cc, 0, 0, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        BorderContainer bcr = new BorderContainer();

        bcr.add(new BLabel(intn.getString("ME.TILESET")), BorderContainer.NORTH, li);
        wTileSet = new TileSetWidget();
        bcr.add(wTileSet, BorderContainer.CENTER, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));
        btnChoose = new BButton(intn.getString("ME.CHOOSETS"));
        btnChoose.addEventLink(CommandEvent.class, this, "chooseTS");
        bcr.add(btnChoose, BorderContainer.SOUTH, li);

        right.add(bcr, 1, 0, new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, new Insets(5, 5, 5, 5), null));

        tabbed.add(right, intn.getString("GUI"));

        script = new ScriptArea();
        tabbed.add(script, intn.getString("SCRIPT"));

        split.add(tabbed, 1);

        enableAll(false);
    }

    private void enableAll(boolean en) {
        btnChoose.setEnabled(en);
        btnRefresh.setEnabled(en);
        spnWidth.setEnabled(en);
        spnHeight.setEnabled(en);
        spnOffset.setEnabled(en);
        spnGap.setEnabled(en);
        wTileSet.setEnabled(en);
        script.setEnabled(en);
    }

    private void refreshGUI(TileSet ts) {
        spnWidth.setValue(ts.getTileWidth());
        spnHeight.setValue(ts.getTileHeight());
        spnOffset.setValue(ts.getOffset());
        spnGap.setValue(ts.getGap());
        wTileSet.setTileSet(ts);
        script.setText(ts.toScript());
    }

    @Override
    public void save() {
        config.setProperty("gui.divider", "" + split.getDividerLocation());
        config.storeConfig();

        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            if (tabbed.getSelectedTab() == 1) {
                LuaTable env = JsePlatform.standardGlobals();
                LuaThread.setGlobals(env);
                HashMap<String, LibFunction> libraries = Project.getInstance().getScriptRegister().getLibraries();

                for (LibFunction lib : libraries.values()) {
                    env.load(lib);
                }

                ScriptTools.getInstance().runScriptString(script.getText(), env);

                if (!env.get(TileSet.TYPE).isnil()) {
                    ts.create(env.get("tileset"));
                }
            }
        }

        generateScriptFile();
    }

    private void refreshTSW() {
        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            ts.setImage(ts.getImage());
            wTileSet.setTileSet(ts);
        }
    }

    private void chooseTS() {
        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            MediaChooseDialog mcd = new MediaChooseDialog(parent);
            Media m = mcd.showDialog(Media.IMAGE);
            if (m != null) {
                ts.setImage(m);
                wTileSet.setTileSet(ts);
            }
        }
    }

    private void tabChanged() {
        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            if (tabbed.getSelectedTab() == 0) {
                LuaTable env = JsePlatform.standardGlobals();
                LuaThread.setGlobals(env);
                HashMap<String, LibFunction> libraries = Project.getInstance().getScriptRegister().getLibraries();

                for (LibFunction lib : libraries.values()) {
                    env.load(lib);
                }

                ScriptTools.getInstance().runScriptString(script.getText(), env);

                if (!env.get("tileset").isnil()) {
                    ts.create(env.get("tileset"));
                    refreshGUI(ts);
                }
            } else {
                ts.setTileHeight((Integer) spnHeight.getValue());
                ts.setTileWidth((Integer) spnWidth.getValue());
                ts.setOffset((Integer) spnOffset.getValue());
                ts.setGap((Integer) spnGap.getValue());
                script.setText(ts.toScript());
            }
        }
    }

    private void selectionChanged() {
        if (list.getSelectedIndex() > -1) {
            enableAll(true);
            TileSet ts = (TileSet) list.getSelectedValue();
            refreshGUI(ts);
        } else {
            enableAll(false);
        }
    }

    private void valueChanged(ValueChangedEvent ev) {
        if (ev.getWidget().equals(spnWidth)) {
            ((TileSet) list.getSelectedValue()).setTileWidth((int) spnWidth.getValue());
        }
        if (ev.getWidget().equals(spnHeight)) {
            ((TileSet) list.getSelectedValue()).setTileHeight((int) spnHeight.getValue());
        }
        if (ev.getWidget().equals(spnOffset)) {
            ((TileSet) list.getSelectedValue()).setOffset((int) spnOffset.getValue());
        }
        if (ev.getWidget().equals(spnGap)) {
            ((TileSet) list.getSelectedValue()).setGap((int) spnGap.getValue());
        }
    }

    private void addTileSet() {
        Internationalization intn = Internationalization.getInstance();
        Database database = Project.getInstance().getDatabase();
        String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("ME.SETID"));
        if (id != null && database.getScriptObject(TileSet.TYPE, id) == null) {
            TileSet ts = new TileSet();
            ts.setID(id);
            ts.setTileHeight(32);
            ts.setTileWidth(32);
            database.putScriptObject(TileSet.TYPE, id, ts);

            list.add(ts);
        }
    }

    private void removeTileSet() {
        Internationalization intn = Internationalization.getInstance();
        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            int r = JOptionPane.showConfirmDialog(split.getComponent(), intn.getString("ME.REMOVETS"), null, JOptionPane.YES_NO_OPTION);
            if (r == JOptionPane.YES_OPTION) {
                Project.getInstance().getDatabase().removeScriptObject(TileSet.TYPE, ts.getID());
                list.remove(list.getSelectedIndex());
                list.clearSelection();
            }
        }
    }

    private void editTileSet() {
        Internationalization intn = Internationalization.getInstance();
        Database database = Project.getInstance().getDatabase();
        if (list.getSelectedIndex() > -1) {
            TileSet ts = (TileSet) list.getSelectedValue();
            String id = JOptionPane.showInputDialog(split.getComponent(), intn.getString("ME.NEWID"));
            if (id != null) {
                database.removeScriptObject(TileSet.TYPE, ts.getID());
                ts.setID(id);
                database.putScriptObject(TileSet.TYPE, id, ts);
                list.repaint();
            }
        }
    }

    private void generateScriptFile() {
        Project project = Project.getInstance();
        String scrpt = "-- OpenRPGStudio TileSet Script --\n"
                + "-- Script is generated by application! Change with care! --\n\n";
        HashMap<String, ScriptObject> scriptMap = project.getDatabase().getScriptMap(TileSet.TYPE);
        if (scriptMap != null) {
            for (ScriptObject o : scriptMap.values()) {
                scrpt += o.toScript() + "\n"
                        + "Tilesets.newTileset(tileset) \n\n";
            }
        }

        ScriptTools.getInstance().saveScriptFile(project.getFolder("scripts").resolve("tilesets.lua"), scrpt);
    }
}
